home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 7 / Amiga Format AFCD07 (Dec 1996, Issue 91).iso / serious / shareware / programming / aros / exec / rawdofmt.c < prev    next >
C/C++ Source or Header  |  1996-09-13  |  10KB  |  424 lines

  1. /*
  2.     (C) 1995-96 AROS - The Amiga Replacement OS
  3.     $Id: rawdofmt.c,v 1.5 1996/09/13 17:51:23 digulla Exp $
  4.     $Log: rawdofmt.c,v $
  5.     Revision 1.5  1996/09/13 17:51:23  digulla
  6.     Use IPTR
  7.  
  8.     Revision 1.4  1996/08/13 13:56:05  digulla
  9.     Replaced __AROS_LA by __AROS_LHA
  10.     Replaced some __AROS_LH*I by __AROS_LH*
  11.     Sorted and added includes
  12.  
  13.     Revision 1.3  1996/08/01 17:41:15  digulla
  14.     Added standard header for all files
  15.  
  16.     Desc:
  17.     Lang: english
  18. */
  19. #include <dos/dos.h>
  20. #include "machine.h"
  21. #include <aros/libcall.h>
  22.  
  23. /*****************************************************************************
  24.  
  25.     NAME */
  26.     #include <clib/exec_protos.h>
  27.  
  28.     __AROS_LH4I(APTR,RawDoFmt,
  29.  
  30. /*  SYNOPSIS */
  31.     __AROS_LHA(STRPTR,    FormatString, A0),
  32.     __AROS_LHA(APTR,      DataStream,   A1),
  33.     __AROS_LHA(VOID_FUNC, PutChProc,    A2),
  34.     __AROS_LHA(APTR,      PutChData,    A3),
  35.  
  36. /*  LOCATION */
  37.     struct ExecBase *, SysBase, 87, Exec)
  38.  
  39. /*  FUNCTION
  40.     printf-style formatting function with callback hook.
  41.  
  42.     INPUTS
  43.     FormatString - Pointer to the format string with any of the following
  44.                stream formatting options allowed:
  45.  
  46.                %[leftalign][minwidth.][maxwidth][size][type]
  47.  
  48.                leftalign - '-' means align left. Default: align right.
  49.                minwidth  - minimum width of field. Defaults to 0.
  50.                maxwidth  - maximum width of field (for strings only).
  51.                    Defaults to no limit.
  52.                size     - 'l' means longword. Defaults to word.
  53.                type     - 'b' BCPL string. A BPTR to a one byte
  54.                        byte count followed by the characters.
  55.                    'c' single character.
  56.                    'd' signed decimal number.
  57.                    's' C string. NUL terminated.
  58.                    'u' unsigned decimal number.
  59.                    'x' unsigned sedecimal number.
  60.  
  61.     DataStream   - Array of the data items.
  62.     PutChProc    - Callback function. Called for each character, including
  63.                the NUL terminator.
  64.     PutChData    - Data propagated to each call of the callback hook.
  65.  
  66.     RESULT
  67.     Pointer to the rest of the DataStream.
  68.  
  69.     NOTES
  70.     The field size defaults to words which may be different from the
  71.     default integer size of the compiler.
  72.  
  73.     EXAMPLE
  74.     build a sprintf style function
  75.  
  76.     static void callback(UBYTE chr, UBYTE **data)
  77.     {   *(*data)++=chr;   }
  78.  
  79.     void my_sprintf(UBYTE *buffer, UBYTE *format, ...)
  80.     {   RawDoFmt(format, &format+1, &callback, &buffer);   }
  81.  
  82.     BUGS
  83.     PutChData cannot be modified from the callback hook.
  84.  
  85.     SEE ALSO
  86.  
  87.     INTERNALS
  88.  
  89.     HISTORY
  90.     22-10-95    created by m. fleischer
  91.  
  92. ******************************************************************************/
  93. {
  94.     __AROS_FUNC_INIT
  95.     /* Cast for easier access */
  96.     ULONG stream=(IPTR)DataStream;
  97.  
  98.     /* As long as there is something to format left */
  99.     while(*FormatString)
  100.     {
  101.     /* Check for '%' sign */
  102.     if(*FormatString=='%')
  103.     {
  104.         /*
  105.         left     - left align flag
  106.         fill     - pad character
  107.         minus     - 1: number is negative
  108.         minwidth - minimum width
  109.         maxwidth - maximum width
  110.         larg     - long argument flag
  111.         width     - width of printable string
  112.         buf     - pointer to printable string
  113.         */
  114.         int left=0;
  115.         int fill=' ';
  116.         int minus=0;
  117.         ULONG minwidth=0;
  118.         ULONG maxwidth=~0;
  119.         int larg=0;
  120.         ULONG width=0;
  121.         UBYTE *buf;
  122.  
  123.         /*
  124.         Number of decimal places required to convert a unsigned long to
  125.         ascii. The formula is: ceil(number_of_bits*log10(2)).
  126.         Since I can't do this here I use .302 instead of log10(2) and
  127.         +1 instead of ceil() which most often leads to exactly the
  128.         same result (and never becomes smaller).
  129.  
  130.         Note that when the buffer is large enough for decimal it's
  131.         large enough for sedecimal as well.
  132.         */
  133. #define CBUFSIZE (sizeof(ULONG)*8*302/1000+1)
  134.         /* The buffer for converting long to ascii */
  135.         UBYTE cbuf[CBUFSIZE];
  136.         ULONG i;
  137.  
  138.         /* Skip over '%' character */
  139.         FormatString++;
  140.  
  141.         /* '-' modifier? (left align) */
  142.         if(*FormatString=='-')
  143.         left=*FormatString++;
  144.  
  145.         /* '0' modifer? (pad with zeros) */
  146.         if(*FormatString=='0')
  147.         fill=*FormatString++;
  148.  
  149.         /* Get minimal width */
  150.         if(*FormatString>='0'&&*FormatString<='9')
  151.         {
  152.         do
  153.             minwidth=minwidth*10+(*FormatString++-'0');
  154.         while(*FormatString>='0'&&*FormatString<='9');
  155.  
  156.         /* Dot following width modifier? */
  157.         if(*FormatString=='.')
  158.         {
  159.             FormatString++;
  160.             /* Get maximum width */
  161.             if(*FormatString>='0'&&*FormatString<='9')
  162.             {
  163.             maxwidth=0;
  164.             do
  165.                 maxwidth=maxwidth*10+(*FormatString++-'0');
  166.             while(*FormatString>='0'&&*FormatString<='9');
  167.             }
  168.         }
  169.         else
  170.         {
  171.             /* No. It was in fact a maxwidth modifier */
  172.             maxwidth=minwidth;
  173.             minwidth=0;
  174.         }
  175.         }
  176.  
  177.         /* 'l' modifier? (long argument) */
  178.         if(*FormatString=='l')
  179.         larg=*FormatString++;
  180.  
  181.         /* Switch over possible format characters. Sets minus, width and buf. */
  182.         switch(*FormatString)
  183.         {
  184.         /* BCPL string */
  185.         case 'b':
  186.             /* Get address, but align datastream first */
  187.             if(LONGALIGN>WORDALIGN)
  188.             stream=(stream+LONGALIGN-1)&~(LONGALIGN-1);
  189.             buf=(UBYTE *)BADDR(*(BPTR *)stream);
  190.             stream+=sizeof(BPTR);
  191.  
  192.             /* Set width */
  193.             width=*buf++;
  194.  
  195.             /* Strings may be modified with the maxwidth modifier */
  196.             if(width>maxwidth)
  197.             width=maxwidth;
  198.             break;
  199.  
  200.         /* signed decimal value */
  201.         case 'd':
  202.         /* unsigned decimal value */
  203.         case 'u':
  204.             {
  205.             ULONG n;
  206.  
  207.             /* Get value */
  208.             if(larg)
  209.             {
  210.                 /* Align datastream */
  211.                 if(LONGALIGN>WORDALIGN)
  212.                 stream=(stream+LONGALIGN-1)&~(LONGALIGN-1);
  213.                 /*
  214.                 For longs reading signed and unsigned
  215.                 doesn't make a difference.
  216.                 */
  217.                 n=*(ULONG *)stream;
  218.                 stream+=sizeof(ULONG);
  219.  
  220.                 /* But for words it may do. */
  221.             }else
  222.                 /*
  223.                 Sorry - it may not: Stupid exec always treats
  224.                 UWORD as WORD even when 'u' is used.
  225.                 */
  226. #ifdef FIX_EXEC_BUGS
  227.                 if(*FormatString=='d')
  228. #else
  229.                 if(1)
  230. #endif
  231.                 {
  232.                 n=*(WORD *)stream;
  233.                 stream+=sizeof(WORD);
  234.                 }else
  235.                 {
  236.                 n=*(UWORD *)stream;
  237.                 stream+=sizeof(UWORD);
  238.                 }
  239.  
  240.             /* Negative number? */
  241.             if(*FormatString=='d'&&(LONG)n<0)
  242.             {
  243.                 minus=1;
  244.                 n=-n;
  245.             }
  246.  
  247.             /* Convert to ASCII */
  248.             buf=&cbuf[CBUFSIZE];
  249.             do
  250.             {
  251.                 ULONG r;
  252.  
  253.                 /*
  254.                 divide 'n' by 10 and get quotient 'n'
  255.                 and remainder 'r'
  256.                 */
  257.                 UDIVMOD10(n,n,r);
  258.                 *--buf=r+'0';
  259.                 width++;
  260.             }while(n);
  261.             }
  262.             break;
  263.  
  264.         /* unsigned sedecimal value */
  265.         case 'x':
  266.             {
  267.             ULONG n;
  268.  
  269.             /* Get value */
  270.             if(larg)
  271.             {
  272.                 /* Align datastream */
  273.                 if(LONGALIGN>WORDALIGN)
  274.                 stream=(stream+LONGALIGN-1)&~(LONGALIGN-1);
  275.                 n=*(ULONG *)stream;
  276.                 stream+=sizeof(ULONG);
  277.             }else
  278.             {
  279.                 n=*(UWORD *)stream;
  280.                 stream+=sizeof(UWORD);
  281.             }
  282.  
  283.             /* Convert to ASCII */
  284.             buf=&cbuf[CBUFSIZE];
  285.             do
  286.             {
  287.                 /*
  288.                 Uppercase characters for lowercase 'x'?
  289.                 Stupid exec original!
  290.                 */
  291.                 *--buf="0123456789ABCDEF"[n&15];
  292.                 n>>=4;
  293.                 width++;
  294.             }while(n);
  295.             }
  296.             break;
  297.  
  298.         /* C string */
  299.         case 's':
  300.             {
  301.             UBYTE *buffer;
  302.  
  303.             /* Get address, but align datastream first */
  304.             if(PTRALIGN>WORDALIGN)
  305.                 stream=(stream+PTRALIGN-1)&~(PTRALIGN-1);
  306.             buf=*(UBYTE **)stream;
  307.             stream+=sizeof(UBYTE *);
  308.  
  309.             /* width=strlen(buf) */
  310.             buffer=buf;
  311.             while(*buffer++)
  312.                 ;
  313.             width=~(buf-buffer);
  314.  
  315.             /* Strings may be modified with the maxwidth modifier */
  316.             if(width>maxwidth)
  317.                 width=maxwidth;
  318.             }
  319.             break;
  320.  
  321.         /* single character */
  322.         case 'c':
  323.             /* Some space for the result */
  324.             buf=cbuf;
  325.             width=1;
  326.  
  327.             /* Get value */
  328.             if(larg)
  329.             {
  330.             /* Align datastream */
  331.             if(LONGALIGN>WORDALIGN)
  332.                 stream=(stream+LONGALIGN-1)&~(LONGALIGN-1);
  333.             *buf=*(ULONG *)stream;
  334.             stream+=sizeof(ULONG);
  335.             }else
  336.             {
  337.             *buf=*(UWORD *)stream;
  338.             stream+=sizeof(UWORD);
  339.             }
  340.             break;
  341.  
  342.         /* '%' before '\0'? */
  343.         case '\0':
  344.             /*
  345.             This is nonsense - but do something useful:
  346.             Instead of reading over the '\0' reuse the '\0'.
  347.             */
  348.             FormatString--;
  349.             /* Get compiler happy */
  350.             buf=NULL;
  351.             break;
  352.  
  353.         /* Convert '%unknown' to 'unknown'. This includes '%%' to '%'. */
  354.         default:
  355.             buf=FormatString;
  356.             width=1;
  357.             break;
  358.         }
  359.         /* Skip the format character */
  360.         FormatString++;
  361.  
  362.         /*
  363.         Now everything I need is known:
  364.         buf     - contains the string to be printed
  365.         width     - the size of the string
  366.         minus     - is 1 if there is a '-' to print
  367.         fill     - is the pad character
  368.         left     - is 1 if the string should be left aligned
  369.         minwidth - is the minimal width of the field
  370.         (maxwidth is already part of width)
  371.  
  372.         So just print it.
  373.         */
  374.  
  375.         /*
  376.         Stupid exec always prints the '-' sign directly before
  377.         the decimals. Even if the pad character is a '0'.
  378.         */
  379. #ifdef FIX_EXEC_BUGS
  380.         /* Print '-' (if there is one and the pad character is no space) */
  381.         if(minus&&fill!=' ')
  382.         RDFCALL(PutChProc,'-',PutChData);
  383. #endif
  384.         /* Pad left if not left aligned */
  385.         if(!left)
  386.         for(i=width+minus;i<minwidth;i++)
  387.             RDFCALL(PutChProc,fill,PutChData);
  388.  
  389.         /* Print '-' (if there is one and the pad character is a space) */
  390. #ifdef FIX_EXEC_BUGS
  391.         if(minus&&fill==' ')
  392. #else
  393.         if(minus)
  394. #endif
  395.         RDFCALL(PutChProc,'-',PutChData);
  396.  
  397.         /* Print body upto width */
  398.         for(i=0;i<width;i++)
  399.         {
  400.         RDFCALL(PutChProc,*buf,PutChData);
  401.         buf++;
  402.         }
  403.  
  404.         /* Pad right if left aligned */
  405.         if(left)
  406.         for(i=width+minus;i<minwidth;i++)
  407.             /* Pad right with '0'? Sigh - if the user wants to! */
  408.             RDFCALL(PutChProc,fill,PutChData);
  409.     }else
  410.     {
  411.         /* No '%' sign? Put the formatstring out */
  412.         RDFCALL(PutChProc,*FormatString,PutChData);
  413.         FormatString++;
  414.     }
  415.     }
  416.     /* All done. Put the terminator out. */
  417.     RDFCALL(PutChProc,'\0',PutChData);
  418.  
  419.     /* Return the rest of the datastream. */
  420.     return (APTR)stream;
  421.     __AROS_FUNC_EXIT
  422. } /* RawDoFmt */
  423.  
  424.